//**********************************************************************
//	Programmer	-	Sujal Bista
//	File Name	-	camera.h
//	Description	:	
//					Camera Functions
//
//**********************************************************************

#ifndef _CAMERA_H_
#define _CAMERA_H_ 

//compile only once
#pragma once

#include "global.h"

class camera
{
	private:

		//dimesnion of the camera
		unsigned int width,height;

		//near and far clipping plane
		float nearPlane,farPlane;

		//The field of view angle, in degrees, in the y-direction
		float fovY,fovX;

		//direction of the camera
		float3 lookAt;

		//up vector of the camera
		float3 up;

		//side vector of the camera
		float3 side;

		//position of the camera
		float3 position;

		//velocity of the camera
		float3 velocity;

		//velocity in various angles
		float rollVelocity,pitchVelocity,yawVelocity;

		//exponential function for smooth camera
		float expo;

		//projection of Camera
		float4x4 projectionMatrix;

		//model view of Camera
		float4x4 viewMatrix;

		//clipping planes
		floatFrustum frust;

		//used for clustering
		int translateX,translateY;
		
	public:
		//constructor
		camera(void);
		
		//set camera axis
		void setAxis(const float3& lookAt,const float3& up,const float3& side);

		//sets size of the camera
		void setSize(const unsigned int& width,const unsigned int& height);

		//sets FOV near plane and far plane
		void setVisibleRange(const float& nearPlane,const float& farPlane,const float& fovY,float fovX=0);

		//sets cluster param
		void setClusterParam(const int &translateX,const int &translateY);

		//set field of view
		void setFOV(const float& fovY,float fovX=0);

		//return viewing direction of the camera
		void setLookAt(const float3 &lookAt){ this->lookAt=lookAt; }

		//return Up of the camera
		void setUp(const float3& up){ this->up=up; }

		//return side of the camera
		void setSide(const float3& side){ this->side=side; }

		//gets near plane
		float getNearPlane(void) const;

		//get far plane
		float getFarPlane(void) const;

		//get field of view in y-axis
		float getFOVY(void) const;

		//get field of view in x-axis
		float getFOVX(void) const;

		//update the position of the camera
		void setPosition(const float3& position);

		//sets the camera params based on position, lookat position and up direction left handed
		void setLookToLH(const float3& eyePosition, const float3& focusPosition,const float3& upDirection);

		//sets the camera params based on position, lookat position and up direction right handed
		void setLookToRH(const float3& eyePosition, const float3& focusPosition,const float3& upDirection);

		//velocity of the camera
		void setVelocity(const float3 &velocity){	this->velocity=velocity;}

		//set roll velocity of the camera
		void setRollVelocity(const float &velocity){	this->rollVelocity=velocity;}

		//set pitch velocity of the camera
		void setPitchVelocity(const float &velocity){	this->pitchVelocity=velocity;}

		//set yaw velocity of the camera
		void setYawVelocity(const float &velocity){	this->yawVelocity=velocity;	}
		
		//return position of the camera
		const float3& getPosition(void) const { return position; }
		
		//return viewing direction of the camera
		const float3& getLookAt(void)const { return lookAt; }

		//return Up of the camera
		const float3& getUp(void)const { return up; }

		//return side of the camera
		const float3& getSide(void)const { return side; }

		//returns width of camera in screen space
		const unsigned int &getWidth(void) const {return width;}

		//returns height of camera in screen space
		const unsigned int &getHeight(void) const {return height;}

		const floatFrustum &getFrustrum(void) const {return frust;};

		//multiplies the view matrix by the given matrix
		void operator*=(const float4x4 &mat) { viewMatrix*=mat;}

		//returns projection matrix
		const float4x4 &getProjectionMatrix(void) const { return  projectionMatrix;}

		//returns view matrix
		const float4x4 &getViewMatrix(void) const { return  viewMatrix;}

		//moves camera along x axis
		void moveX(const float& amount);

		//moves camera along y axis
		void moveY(const float& amount);

		//moves camera along z axis
		void moveZ(const float& amount);

		//rotate camera along X axis
		void rotateX(const float& theta);

		//rotate camera along Y axis
		void rotateY(const float& theta);

		//rotate camera along Z axis
		void rotateZ(const float& theta);

		//rotate camera along camera's X axis
		void pitch(const float& theta);

		//rotate camera along camera's Y axis
		void yaw(const float& theta);

		//rotate camera along camera's Z axis
		void roll(const float& theta);

		//rotate camera around camera's x axis looking at look at point
		void pitch3RDPerson(const float& theta, float lookAtDistance);

		//rotate camera around camera's y axis looking at look at point
		void yaw3RDPerson(const float& theta, float lookAtDistance);

		//rotate camera around camera's z axis looking at look at point
		void roll3RDPerson(const float& theta, float lookAtDistance);

		//update the camera
		void update(const float& dt);

		//sets camera mode true for 1st person
		void setCameraMode(const bool& isFirstPersonMode);

		//destructor
		~camera();

		//compute projection matrix right hand
		void computeProjectionMatrixRH(void);

		//compute projection matrix left hand
		void computeProjectionMatrixLH(void);

		//compute view matrix right hand
		void computeViewMatrixRH(void);

		//compute view matrix left hand
		void computeViewMatrixLH(void);

		//calculate frustum
		void updateViewFrustum(void);

		//draws the camera
		void drawCamera(void) const;

		//project world coordinate to screen coordinate
		float3 worldToScreen(const float3 &world) const;

		//project screen coordinate to world coordinate
		float3 screenToWorld(const float3 &screen) const;

		//project world coordinate to camera coordinate
		float3 worldToCamera(const float3 &world) const;

		//project camera coordinate to world coordinate
		float3 cameraToWorld(const float3 &camera) const;
};


#endif
